package com.one.rope.mvp.basecore.util;

import org.apache.commons.codec.DecoderException;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
import org.apache.commons.codec.binary.Hex;

/**
 * @ClassName AesUtils
 * @Description CBC模式的AES加密
 * @Author gavin
 * @Date 2019-02-25 11:31
 * @Version 1.0
 **/
public class AesUtils {

  /**
   * 加密算法,CBC模式
   */
  private static final String ALGORITHM = "AES/CBC/PKCS5Padding";

  /**
   * key
   */
  private static final String KEY = "1dc5d72e5b3a097cca875cce84c402946";

  /**
   * 用于生成随机iv
   */
  private static byte[] getSecureRandom(int length) throws NoSuchAlgorithmException {
    byte[] randomByte = new byte[length];

    SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
    secureRandom.nextBytes(randomByte);

    return randomByte;
  }

  /**
   * 不同长度的种子seed,生成一样长度的key
   */
  private static byte[] getSecretKey(byte[] seed) throws NoSuchAlgorithmException {
    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");

    secureRandom.setSeed(seed);
    keyGenerator.init(256, secureRandom); // 256 bits or 128 bits,192bits
    SecretKey secretKey = keyGenerator.generateKey();
    byte[] result = secretKey.getEncoded();

    return result;
  }

  private static byte[] aesCipher(byte[] source, byte[] key, byte[] iv, int mode)
      throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
      InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
    IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

    Cipher cipher = Cipher.getInstance(ALGORITHM);
    cipher.init(mode, secretKeySpec, ivParameterSpec);

    return cipher.doFinal(source);
  }

  /**
   * @return String
   * @Author gavin(wcggavin @ gmail.com)
   * @Description 加密
   * @Date 2019-02-25 11:35
   * @Params source
   **/
  public static String encrypt(String source) throws Exception {
    try {
      byte[] iv = getSecureRandom(16);

      byte[] resultByte = aesCipher(source.getBytes(StandardCharsets.UTF_8),
          getSecretKey(KEY.getBytes()), iv,
          Cipher.ENCRYPT_MODE);

      Base64.Encoder encoder = Base64.getEncoder();

      // iv值拼接在密文值后面
      return encoder.encodeToString(resultByte).concat(new String(Hex.encodeHex(iv)));
    } catch (NoSuchAlgorithmException e) {
      throw new Exception(e.getMessage());
    } catch (InvalidKeyException e) {
      throw new Exception(e.getMessage());
    } catch (NoSuchPaddingException e) {
      throw new Exception(e.getMessage());
    } catch (InvalidAlgorithmParameterException e) {
      throw new Exception(e.getMessage());
    } catch (IllegalBlockSizeException e) {
      throw new Exception(e.getMessage());
    } catch (BadPaddingException e) {
      throw new Exception(e.getMessage());
    }
  }

  /**
   * @return String
   * @Author gavin(wcggavin @ gmail.com)
   * @Description 解密
   * @Date 2019-02-25 11:35
   * @Params source
   **/
  public static String decrypt(String source) throws Exception {
    try {
      // 先分别解析密文值和iv值
      String message = source.substring(0, source.length() - 32);
      String iv = source.substring(source.length() - 32, source.length());

      Base64.Decoder decoder = Base64.getDecoder();

      byte[] resultByte = aesCipher(decoder.decode(message), getSecretKey(KEY.getBytes()),
          Hex.decodeHex(iv.toCharArray()), Cipher.DECRYPT_MODE);

      return new String(resultByte, StandardCharsets.UTF_8);
    } catch (InvalidKeyException e) {
      throw new Exception(e.getMessage());
    } catch (NoSuchAlgorithmException e) {
      throw new Exception(e.getMessage());
    } catch (NoSuchPaddingException e) {
      throw new Exception(e.getMessage());
    } catch (InvalidAlgorithmParameterException e) {
      throw new Exception(e.getMessage());
    } catch (IllegalBlockSizeException e) {
      throw new Exception(e.getMessage());
    } catch (BadPaddingException e) {
      throw new Exception(e.getMessage());
    } catch (DecoderException e) {
      throw new Exception(e.getMessage());
    }
  }

}
